
/**
 * 高德积木
 * desc: 地图看累了，玩会儿游戏，休息一下吧  ^_^!
 * by: wmlgl
 * url: http://www.oschina.net/
 * src: 
 */
function BuildingGame(_options) {
	var COLORS = ['red', 'yellow', 'blue', 'green', 'orange'];
	var TRANSFORM = [// 定义方块的几种变形
		[// T: [[ point, point, point, point], width]
			[[ [0,0], [1,0], [2,0], [1,1]], 3 ], 
			[[ [1,0], [1,1], [1,2], [0,1]], 2 ],
			[[ [0,1], [1,1], [2,1], [1,0]], 3 ],
			[[ [0,0], [0,1], [0,2], [1,1]], 2 ]
		],
		[// I:
		    [[ [0,0], [0,1], [0,2], [0,3] ], 1 ],
		    [[ [0,0], [1,0], [2,0], [3,0] ], 4 ]
		],
		[// J:
		    [[ [1,0], [1,1], [1,2], [0,2] ], 2 ],
		    [[ [2,1], [1,1], [0,1], [0,0] ], 3 ],
		    [[ [0,2], [0,1], [0,0], [1,0] ], 2 ],
		    [[ [0,0], [1,0], [2,0], [2,1] ], 3 ]
		],
		[// L:
		    [[ [0,0], [0,1], [0,2], [1,2] ], 2 ],
		    [[ [2,0], [1,0], [0,0], [0,1] ], 3 ],
		    [[ [1,2], [1,1], [1,0], [0,0] ], 2 ],
		    [[ [0,1], [1,1], [2,1], [2,0] ], 3 ]
		],
		[// S:
			[[ [2,0], [1,0], [1,1], [0,1] ], 3 ],
			[[ [0,0], [0,1], [1,1], [1,2] ], 2 ]
		],
		[// Z:
			[[ [0,0], [1,0], [1,1], [2,1] ], 3 ],
			[[ [0,2], [0,1], [1,1], [1,0] ], 2 ]
		],
		[// O:
		    [[ [0,0], [1,0], [0,1], [1,1] ], 2 ]
		]
	];
	
	if(!_options || !_options.map) {
		throw new Error('require options.map');
	}
	
	//配置
	var options = {
		map: _options.map,//地图对象
		zIndex: _options.zIndex || 99,//游戏UI的zIndex
		pointSize: _options.pointSize || 15,//方块点大小
		horizontalNum:  _options.horizontalNum || 6,//方块点水平个数
		verticalNum:  _options.verticalNum || 10,//方块点垂直个数
		startX:  _options.startX || 5,//在什么位置开始绘制游戏UI
		startY:  _options.startY || 5,
		scoreWidth:  _options.scoreWidth || 80
	};
	
	var speed = 3;//(f/s)
	var mapKeyboardEnable = null;
	var lastEventRefeshTime = 0;

	//画布
	var canvasX = options.startX;
	var canvasY = options.startY;
	var canvasX2 = canvasX + options.horizontalNum * options.pointSize;
	var canvasY2 = canvasY + options.verticalNum * options.pointSize;
	
	//分数
	var score = 0;
	var scoreLines = new Array();
	var showClearAnimate = false;
	var clearColor = 0;
	
	//地图
	var pointsMap = null;
	var cachePolygon = [];
	var usingPolygon = [];
	var curr = null;
	var next = null;
	var isPause = false;
	var isDestroy = false;
	var isGameOver = false;
	
	//游戏UI
	var canvasBorder = new AMap.Polygon({
		map: options.map, 
		path: [],
		zIndex: options.zIndex
	});
	var scoreBorder = new AMap.Polygon({
		map: options.map, 
		path: [],
		zIndex: options.zIndex
	});
	var nextBorder = new AMap.Polygon({
		map: options.map, 
		path: [],
		zIndex: options.zIndex
	});
	var scoreMarker = new AMap.Marker({
		map: options.map, 
		content: '0',
		zIndex: options.zIndex
	});
	var gameOverMarker = new AMap.Marker({
		map: options.map,
		content: '<div style="background: orange;padding:10px 30px">Game Over!</div>',
		zIndex: options.zIndex,
		visible: false
	});
	var buttonDiv = document.createElement('div');
	buttonDiv.style.cssText = 'background: #FFF;border:1px solid #ccc;width: 60px;padding:0px;color:blue;text-align:center;font-size:13px;';
	buttonDiv.innerHTML = '<a title="暂停/开始">开始</a> <a title="关闭" style="color:red">×</a>';
	
	var buttonMarker = new AMap.Marker({
		map: options.map,
		content: buttonDiv,
		zIndex: options.zIndex,
		visible: true
	});

	var that = this;
	buttonDiv.children[0].onclick=function() {
		if(isPause) {
			that.start();
			this.innerHTML = "暂停";
		} else {
			that.pause();
			this.innerHTML = "开始";
		}
	};
	buttonDiv.children[1].onclick=function() {
		that.destroy();
	};
	
	//开始游戏
	this.start = function() {
		if(isDestroy) {
			throw new Error('this instance is destroyed!');
		}
		if(isPause && !isGameOver) {
			isPause = false;
			mainLoop();
		} else {
			curr = newBuilding();
			next = newBuilding();
			createPointsMap();
			mainLoop();
		}
		
		mapKeyboardEnable = options.map.getStatus().keyboardEnable;
		options.map.setStatus({keyboardEnable:false});
		isGameOver = false;
		gameOverMarker.hide();
		(window.addEventListener||window.attachEvent)('keydown', onKeyDown);
	};
	
	//结束游戏
	this.pause = function() {
		options.map.setStatus({keyboardEnable:mapKeyboardEnable});
		isPause = true;
		(window.removeEventListener||window.dettachEvent)('keydown', onKeyDown);
	};

	//销毁
	this.destroy = function() {
		this.pause();
		isDestroy = true;
		isPause = true;
		pointsMap = null;
		curr = null;
		next = null;
		clearAll(cachePolygon);
		clearAll(usingPolygon);
		clearAll([canvasBorder,
		          scoreBorder,
		          scoreMarker,
		          gameOverMarker, buttonMarker]);
	};
	
	//键盘控制
	function onKeyDown(e) {
		
		if(isGameOver) {
			return;
		}
		
		if(e.keyCode == 38) { //up
			curr.dir+=1;
			//碰撞检测
			checkTransformHit();
		} else if(e.keyCode == 40) {// down
			curr.y+=0.45;
			//撞击检测
			checkHit();
		} else if(e.keyCode == 37) {// left
			//碰撞检测
			if(!isHorizontalHit(curr.x-1)) {
				curr.x--;
			}
		} else if(e.keyCode == 39) {// right
			//碰撞检测
			if(!isHorizontalHit(curr.x+1)) {
				curr.x++;
			}
		} else {
			return true;
		}
		
		if(new Date.now() - lastEventRefeshTime > 50) {
			lastEventRefeshTime = new Date.now();
			updateMap();
		}
		return false;
	}
	
	//更新地图
	function updateMap() {
		//缓存使用中的Polygon
		cachePolygon = cachePolygon.concat(usingPolygon);
		clearAll(usingPolygon);
		
		//游戏UI
		canvasBorder.setOptions({
			nap: options.map,
			path: [
				options.map.containerToLngLat(new AMap.Pixel(canvasX, canvasY)),
				options.map.containerToLngLat(new AMap.Pixel(canvasX2, canvasY)),
				options.map.containerToLngLat(new AMap.Pixel(canvasX2, canvasY2)),
				options.map.containerToLngLat(new AMap.Pixel(canvasX, canvasY2))
			],
			strokeColor: 'green',
			fillColor: '#FFF',
			fillOpacity: 0.8,
			strokeWeight: 2,
			zIndex: options.zIndex-1
		});
		scoreBorder.setOptions({
			nap: options.map,
			path: [
				options.map.containerToLngLat(new AMap.Pixel(canvasX2 + 5, canvasY)),
				options.map.containerToLngLat(new AMap.Pixel(canvasX2 + options.scoreWidth, canvasY)),
				options.map.containerToLngLat(new AMap.Pixel(canvasX2 + options.scoreWidth, canvasY2)),
				options.map.containerToLngLat(new AMap.Pixel(canvasX2 + 5, canvasY2)),
			],
			strokeColor: 'orange',
			fillColor: '#FFF',
			fillOpacity: 0.8,
			strokeWeight: 2,
			zIndex: options.zIndex-1
		});
		nextBorder.setOptions({
			nap: options.map,
			path: [
				options.map.containerToLngLat(new AMap.Pixel(canvasX2 + 10, canvasY + 5)),
				options.map.containerToLngLat(new AMap.Pixel(canvasX2 + options.scoreWidth - 5, canvasY + 5)),
				options.map.containerToLngLat(new AMap.Pixel(canvasX2 + options.scoreWidth - 5, canvasY + 6 * options.pointSize)),
				options.map.containerToLngLat(new AMap.Pixel(canvasX2 + 10, canvasY + 6 * options.pointSize)),
			],
			strokeColor: '#EEE',
			fillColor: '#FEFEFE',
			strokeWeight: 1,
			zIndex: options.zIndex
		});
		
		scoreMarker.setPosition(options.map.containerToLngLat(new AMap.Pixel(canvasX2 + 18, 40 + (canvasY + 6 * options.pointSize))));
		scoreMarker.setContent('<div style="width:'+(options.scoreWidth - 15)+'px;line-height:20px;font-size:13px;color:blue;text-align:center;border:1px solid #eee;background:#FFF8DC">'+score+'</div>');
		
		buttonMarker.setPosition(options.map.containerToLngLat(new AMap.Pixel(canvasX2 + 18, 65 + (canvasY + 6 * options.pointSize))));
		buttonDiv.style.width = (options.scoreWidth - 15)+"px";
		
		gameOverMarker.setPosition(options.map.getCenter());
		
		//下一个方块
		var nextDir = next.dir % TRANSFORM[ next.type ].length;
		var nextPoints = TRANSFORM[ next.type ][ nextDir ][ 0 ];
		var width = TRANSFORM[ next.type ][ nextDir ][ 1 ];
		
		for (var i = 0; i < nextPoints.length; i++) {
			var x = options.horizontalNum + 4 - width/2 + nextPoints[ i ][ 0 ];
			var y = 1.2 + nextPoints[ i ][ 1 ];
			drawPoint(x, y, next.color, 1);
		}
		
		//地图
		for (var y = 0; y < pointsMap.length; y++) {
			var hPoints = pointsMap[ y ];
			for (var x = 0; x < hPoints.length; x++) {
				if(hPoints[ x ][ 0 ] == 1) {
					drawPoint(x, y, hPoints[ x ][ 1 ], hPoints[ x ][ 2 ]);
				}
			}
		}
		
		//运动中的方块
		var currDir = curr.dir % TRANSFORM[ curr.type ].length;
		var currPoints = TRANSFORM[ curr.type ][ currDir ][ 0 ];
		
		for (var i = 0; i < currPoints.length; i++) {
			var x = curr.x + currPoints[ i ][ 0 ];
			var y = Math.floor(curr.y) + currPoints[ i ][ 1 ];
			drawPoint(x, y, curr.color, 1);
		}
	}
	
	//在地图中画一个点块
	function drawPoint(x, y, color, opacity) {
		var polygon = getCachePolygon();
		polygon.setOptions({
			map: options.map,
			path: point2path(x, y),
			fillColor: COLORS[ color ],
			fillOpacity: opacity,
			strokeWeight: 2,
			strokeColor: '#999',
			strokeOpacity: opacity,
			zIndex: options.zIndex
		});
		usingPolygon.push(polygon);
	}
	
	//获取一个缓存的Polygon
	function getCachePolygon() {
		var polygon = cachePolygon.pop();
		if(!polygon) {
			polygon = new AMap.Polygon({
				map: options.map,
				path: []
			});
		}
		return polygon;
	}
	
	//清空给出的覆盖物
	function clearAll(overlays) {
		while(overlays.length) {
			overlays.pop().setMap( null );
		}
	}
	
	//方块点转经纬度数组
	function point2path(x, y) {
		x = x*options.pointSize+canvasX;
		y = y*options.pointSize+canvasY;
		return [  options.map.containerToLngLat(new AMap.Pixel(x, y))
		         ,options.map.containerToLngLat(new AMap.Pixel(x+options.pointSize, y))
		         ,options.map.containerToLngLat(new AMap.Pixel(x+options.pointSize, y+options.pointSize))
		         ,options.map.containerToLngLat(new AMap.Pixel(x, y+options.pointSize))
		       ];
	}
	
	//主循环
	function mainLoop() {
		if( isDestroy ) {
			return;
		}
		setTimeout(mainLoop, 1000/speed);
		
		if(!isPause) {
			if(showClearAnimate) {
				//显示清除动画
				clearAnimate();
				
			} else {

				//移动方块
				curr.y += 0.2;
				
				//撞击检测
				checkHit();
			}
		}
		
		//更新地图
		updateMap();
	}
	
	///撞击检测
	function checkHit() {
		var dir = curr.dir % TRANSFORM[ curr.type ].length;
		var points = TRANSFORM[ curr.type ][ dir ][ 0 ];
		var isHit = false;
		for (var i = 0; i < points.length; i++) {
			var x = points[i][0] + curr.x;
			var y = points[i][1] + Math.floor(curr.y);
			//这点是边界或非空
			if(!pointsMap[ y ] || pointsMap[ y ][ x ][ 0 ] == 1) {
				isHit = true;
				break;
			}
		}

		if(isHit) {
			curr.y = Math.min(Math.max(0, Math.floor(curr.y-1)), options.verticalNum-1);
			//固定
			add2map();
			
			//算分
			checkScore();

			//如果方块的入口被填充则游戏结束
			var startPoint = pointsMap[ 0 ][ Math.floor(options.horizontalNum/2) - 1 ][ 0 ];
			var startPoint2 = pointsMap[ 0 ][ Math.floor(options.horizontalNum/2) ][ 0 ];
			var startPoint3 = pointsMap[ 0 ][ Math.floor(options.horizontalNum/2)+ 1 ][ 0 ];
			if(startPoint == 1 || startPoint2 == 1|| startPoint3 == 1) {
				isGameOver = true;
				isPause = true;
				
				gameOverMarker.show();
				buttonDiv.children[0].innerHTML = '开始';
			}
			
			//新增方块
			curr = next;
			next = newBuilding();
		}
	}
	
	//水平碰撞检测
	function isHorizontalHit(newX) {
		var dir = curr.dir % TRANSFORM[ curr.type ].length;
		var points = TRANSFORM[ curr.type ][ dir ][ 0 ];
		for (var i = 0; i < points.length; i++) {
			var x = points[i][0] + newX;
			var y = points[i][1] + Math.floor(curr.y);
			//这点是边界或非空
			if(!pointsMap[ y ][ x ] || pointsMap[ y ][ x ][ 0 ] == 1) {
				return true;
			}
		}
		return false;
	}
	
	//变形时碰撞检测
	function checkTransformHit() {
		var dir = curr.dir % TRANSFORM[ curr.type ].length;
		var width = TRANSFORM[ curr.type ][ dir ][ 1 ];
		if(curr.x + width >= options.horizontalNum) {
			curr.x = options.horizontalNum - width;
		}
	}
	
	//添加到地图
	function add2map() {
		var dir = curr.dir % TRANSFORM[ curr.type ].length;
		var points = TRANSFORM[ curr.type ][ dir ][ 0 ];
		//设置方块所在点
		for (var i = 0; i < points.length; i++) {
			var x = points[i][0] + curr.x;
			var y = points[i][1] + Math.floor(curr.y);
			pointsMap[ y ][ x  ][ 0 ] = 1;
			pointsMap[ y ][ x  ][ 1 ] = curr.color;
			pointsMap[ y ][ x  ][ 2 ] = 1;
		}
	}
	
	//检查分数
	function checkScore() {
		scoreLines.length = 0;
		
		for (var y = 0; y < pointsMap.length; y++) {
			var hPoint = pointsMap[ y ];
			var hasScore = true;
			for (var x = 0; x < hPoint.length; x++) {
				if(hPoint[ x ][ 0 ] == 0) {
					hasScore = false;
					break;
				}
			}
			if(hasScore) {
				scoreLines.push( y );
			}
		}
		//算分,显示清除动画
		if(scoreLines.length > 0) {
			calcScore();
			showClearAnimate = true;
		}
	}
	
	//计算得分
	function calcScore() {
		score += scoreLines.length*(5 + Math.floor(score / 100));
		
		//加速
		if(score % 200 == 0) {
			speed = Math.max(20, speed + 1);
		}
	}
	
	//消除动画
	function clearAnimate() {
		if(clearColor > speed) {
			removeScoreLines();
			clearColor = 0;
			showClearAnimate = false;
		}
		clearColor++;
		
		for (var i = 0; i < scoreLines.length; i++) {
			var hPoints = pointsMap[ scoreLines[i] ];
			for (var x = 0; x < hPoints.length; x++) {
				//设置透明度(闪烁)
				hPoints[ x ][ 2 ] = 1-(clearColor%(speed/2))/speed;
			}
		}
	}
	
	//消除得分的行
	function removeScoreLines() {
		for (var i = 0; i < scoreLines.length; i++) {
			var hPoints = [];
			pointsMap.splice(scoreLines[ i ],1);
			//补充一个空白行
			for (var j = 0; j < options.horizontalNum; j++) {
				hPoints[j] = [0, 0, 1];// 空白,颜色,透明度
			}
			pointsMap.splice(0, 0, hPoints);
		}
	}
	
	//创建地图
	function createPointsMap() {
		pointsMap = [];
		for (var i = 0; i < options.verticalNum; i++) {
			var hPoints = [];
			for (var j = 0; j < options.horizontalNum; j++) {
				hPoints[j] = [0, 0, 1];// 空白,颜色,透明度
			}
			pointsMap[i] = hPoints;
		}
	}
	
	//新建方块
	function newBuilding() {
		var result = {
			color: Math.round(Math.random()*(COLORS.length-1)),
			type: Math.round(Math.random()*(TRANSFORM.length-1)),
			dir: Math.round(Math.random()*9),
			x: 0,
			y: 0
		};
		
		var dir = result.dir % TRANSFORM[ result.type ].length;
		var width = TRANSFORM[ result.type ][ dir ][ 1 ];
		
		result.x = Math.floor((options.horizontalNum - width) / 2 - 1);
		return result;
	}
	
	this.start();
	this.pause();
}